package com.itlong.cloud.intercept;

import com.alibaba.fastjson.JSONObject;
import com.itlong.cloud.POJO.VO.MessageVO;
import com.itlong.cloud.PlatformConstants;
import com.itlong.cloud.enumerate.ecode.BaseErrorCodeEnum;
import com.itlong.cloud.utils.encrypt.DesEncryptUtil;
import com.itlong.cloud.utils.encrypt.Md5EncryptUtil;
import com.itlong.cloud.utils.encrypt.UserTokenUtil;
import com.itlong.cloud.utils.network.http.HttpProtocolUtil;
import com.itlong.cloud.utils.node.JsonUtil;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <desc>
 *      物业app请求入参与返回拦截器
 * </desc>
 *
 * @createDate2019/12/24
 **/
@Aspect
@Component
@Order(0)
public class PropertyAppParamIntercept {
    private static Logger LOG = LoggerFactory.getLogger(PropertyAppParamIntercept.class);

    @Pointcut("execution(* com.itlong.cloud.controller..*.*(..)) && !@annotation(com.itlong.cloud.annotation.UnParamEncrypt)")
    public void executeService(){}

    @Value("${"+ PlatformConstants.PROPERTY_APP_USER_TOKEN_KEY +"}")
    String PROPERTY_APP_USER_TOKEN_KEY;

    @Value("${"+ PlatformConstants.PROPERTY_APP_MD5_ENCRYPT_KEY +"}")
    String PROPERTY_APP_MD5_TOKEN_KEY;

    /**
     * <desc>
     *      请求入参和返回拦截器方法
     * </desc>
     *
     * @author wangzhi
     * @createDate 2020/01/07
     */
    @Around("executeService()")
    public Object handleParam(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //入参密文
        String encryptScript = request.getParameter("ciphertext");
        if (StringUtils.isBlank(encryptScript)) {
            //加密串未携带，参数异常
            return getEncryptResult(new MessageVO(BaseErrorCodeEnum.ERROR.getErrorCode()));
        } else {
            //解密入参
            String encryptResult = UserTokenUtil.getPassword(encryptScript, PROPERTY_APP_USER_TOKEN_KEY);
            if (StringUtils.isBlank(encryptResult)) {
                // 因ios上传图片时字符串过长，加密只能分段加密，这边一次解密会有乱码存在，图片无法解码，故ios上传图片时部分加密再拼接原文，这边截取之后解密部分加密部分再连接原字符串
                String[] encryptScriptArr = encryptScript.split("5278MMN5412365UH");
                encryptResult = UserTokenUtil.getPassword(encryptScriptArr[0], PROPERTY_APP_USER_TOKEN_KEY);
                if (StringUtils.isBlank(encryptResult)) {
                    return getEncryptResult(new MessageVO(BaseErrorCodeEnum.PARAM_ERR.getErrorCode()));
                }
                encryptResult = encryptResult + encryptScriptArr[1];
            }
            encryptResult = encryptResult.trim();
            encryptResult = encryptResult.replaceAll("&quot;", "\"");
            // 入参转map
            Map<String, Object> params = JsonUtil.toObject(encryptResult, HashMap.class);
            if (params == null) {
                return getEncryptResult(new MessageVO(BaseErrorCodeEnum.PARAM_ERR.getErrorCode()));
            }
            // 进行验签
            String sign = params.get("sign") == null ? "" : params.get("sign").toString();
            params.remove("sign"); //移除签名
            Object token = params.remove("token");// 移除token进行验签
            if (!Md5EncryptUtil.checkAppSignValue(params, sign, PROPERTY_APP_MD5_TOKEN_KEY, "")) {
                return getEncryptResult(new MessageVO(BaseErrorCodeEnum.CHECK_SIGN_ERR.getErrorCode()));
            }

            // token放回map中
            params.put("token", token == null ? "" : token);
            // 将参数放入请求域中
            HttpProtocolUtil.setRequestAttributes(params, request);

            Object result = joinPoint.proceed();

            //签名且加密
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
            Date date = new Date();
            String createTime = df.format(date);
            String resultJson = JsonUtil.toJSON(result);
            JSONObject json = new JSONObject();
            Map<String, Object> jsonToMap = new HashMap<>();
            jsonToMap.put("data", resultJson);
            sign = Md5EncryptUtil.cloudDeviceMd5(jsonToMap, createTime, PROPERTY_APP_MD5_TOKEN_KEY);
            json.put("allData", resultJson);
            json.put("createTime", createTime);
            json.put("sign", sign);
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("ciphertext", Base64.encode(DesEncryptUtil.Encrypt(URLEncoder.encode(new String(json.toJSONString().getBytes()), "utf-8").getBytes(), PROPERTY_APP_USER_TOKEN_KEY)));

            return resultMap;
        }
    }

    /**
     * <desc>
     *      返回加密内容
     * </desc>
     *
     * @author wangzhi
     * @createDate 2020/01/07
     */
    private Map<String, Object> getEncryptResult(Object result) throws Exception {
        JSONObject json = new JSONObject();
        Date date = new Date();
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        String createTime = df.format(date);
        Map<String, Object> jsonToMap = new HashMap<>();
        jsonToMap.put("data", JsonUtil.toJSON(result));
        String sign = Md5EncryptUtil.cloudDeviceMd5(jsonToMap, createTime, PROPERTY_APP_MD5_TOKEN_KEY);
        json.put("allData", JsonUtil.toJSON(result));
        json.put("createTime", createTime);
        json.put("sign", sign);
        String encryptResult = Base64.encode(DesEncryptUtil.Encrypt(URLEncoder.encode(new String(json.toJSONString().getBytes()), "utf-8").getBytes(), PROPERTY_APP_USER_TOKEN_KEY));
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("ciphertext", encryptResult);
        return resultMap;
    }
}
